﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocketChat.Common.Database;
using WebSocketChat.Common.Server;

namespace WebSocketChat.Common.Client
{
    public class ClientSocket
    {
        private ManualResetEvent timeoutObject = new ManualResetEvent(false);
        private bool IsConnectionSuccessful = false;
        public bool IsReceive = true;
        public event Action<MessageArriveEventArgs> MessageArrive;
        public Socket Socket { get; private set; }

        public ChatUser User { get; private set; }
        public ClientSocket()
        {
        }

        public ClientSocket(ChatUser User)
        {
            this.User = User;
        }

        private void WriteLog(string info)
        {
            Console.WriteLine(DateTime.Now.ToString() + " " + info);
        }

        // 检查一个Socket是否可连接
        public bool IsSocketConnected(Socket client)
        {
            bool blockingState = client.Blocking;
            try
            {
                byte[] tmp = new byte[1];
                client.Blocking = false;
                client.Send(tmp, 0, 0);
                return false;
            }
            catch (SocketException e)
            {
                // 产生 10035 == WSAEWOULDBLOCK 错误，说明被阻止了，但是还是连接的
                if (e.NativeErrorCode.Equals(10035))
                    return false;
                else
                    return true;
            }
            finally
            {
                client.Blocking = blockingState;    // 恢复状态
            }
        }

        public void Connect(string ip, int port)
        {
            IPAddress ipAddr = IPAddress.Parse(ip);
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, port);
            Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            Socket.Connect(ipEndPoint);
        }

        public void Connect(string ip, int port, int timeout)
        {
            IPAddress ipAddr = IPAddress.Parse(ip);
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, port);
            Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            timeoutObject.Reset();
            Socket.BeginConnect(ipEndPoint, new AsyncCallback(CallbackMethod), Socket);
            if (timeoutObject.WaitOne(timeout, false))
            {
                if (!IsConnectionSuccessful)
                {
                    throw new TimeoutException("连接超时...");
                }
                else
                {
                    Thread thread = new Thread(new ThreadStart(Receive));
                    thread.Start();
                }
            }
            else
            {
                throw new TimeoutException("连接超时...");
            }
        }

        public void Receive()
        {
            while (IsReceive)
            {
                byte[] buffer = new byte[1024 * 1024];
                int count = 0;
                try
                {
                    count = Socket.Receive(buffer);
                }
                catch (SocketException ex)
                {
                    if (ex.SocketErrorCode == SocketError.ConnectionReset)
                    {
                        return;
                    }
                    else
                    {
                        WriteLog(ex.Message);
                    }
                }
                if (count == 0)
                {
                    return;
                }
                MessageArrive?.Invoke(new MessageArriveEventArgs() { Message = MessageConvert.Parse(buffer, count) });

            }
        }

        public void Close()
        {
            Socket.Close();
        }

        private void CallbackMethod(IAsyncResult result)
        {
            try
            {
                IsConnectionSuccessful = false;
                var socket = result.AsyncState as Socket;
                if (socket != null)
                {
                    socket.EndConnect(result);
                    IsConnectionSuccessful = true;
                }
            }
            catch (Exception ex)
            {
                IsConnectionSuccessful = false;
            }
            finally
            {
                timeoutObject.Set();
            }
        }

        public void Send(IMessage msg)
        {
            Socket.Send(msg.Serialize(true));
        }
    }
}
